iT邦幫忙

2026 iThome 鐵人賽

DAY 15
0
JavaScript

React 觀念架構:從js 基礎到Hook 底層邏輯 系列 第 15 篇

Day 15 Concurrent React 實戰應用:useTransition 與 useDeferredValue 防手震切換(Debounce)

  • 分享至 

  • xImage
  •  

今天學習seTransition 與 useDeferredValue。這兩個 Hook 能讓我們以「聲明式」的方式,將特定 UI 更新標記為「非緊急任務(Non-urgent Updates)」,完美解決打字輸入框 lag、巨量列表渲染卡頓等效能痛點!

一、 為什麼傳統的防手震 (Debounce) 還不夠好?

在過去,面對「輸入框搜尋帶動下方巨量列表重繪」的卡頓問題,開發者常用的手法是 lodash.debounce 或 setTimeout:

// 傳統 Debounce:強制延遲 300ms 執行
const handleSearch = debounce((query) => {
  setSearchQuery(query);
}, 300);

傳統 Debounce 的兩大局限:

  1. 人為的人工延遲:不管使用者的電腦效能多強,都要死板地等待 300ms 才能看到結果,缺乏靈活性。

  2. 一旦執行依然卡頓:300ms 時間一到,列表渲染依然會同步且不可中斷地佔用主執行緒,造成畫面瞬間凍結。

二、 useTransition:標記狀態更新為低優先級

當你有自己可以控制的狀態變更函數 (setState),且希望它不要阻塞 UI 輸入時,使用 useTransition。

語法範例:
useTransition 回傳一個包含兩個元素的陣列:

  • isPending:布林值,代表背景的低優先級渲染是否正在進行中(可用於顯示 Spinner)。

  • startTransition:函數,將傳入的狀態更新封裝為非緊急任務。

import { useState, useTransition } from 'react';

function SearchPage() {
  const [inputVal, setInputVal] = useState('');
  const [listQuery, setListQuery] = useState('');
  const [isPending, startTransition] = useTransition();

  const handleChange = (e) => {
    // 1. 緊急任務:立即更新 Input,確保打字 100% 流暢
    setInputVal(e.target.value);

    // 2. 非緊急任務:包裹在 startTransition 中,允許被打字中斷
    startTransition(() => {
      setListQuery(e.target.value);
    });
  };

  return (
    <div>
      <input value={inputVal} onChange={handleChange} />
      {isPending && <p>載入列表中...</p>}
      <HeavyList query={listQuery} />
    </div>
  );
}

三、 useDeferredValue:延遲接收到的 Props 或 Value

如果你無法直接存取 setState(例如值是從父組件傳入的 Props,或是第三方套件提供的狀態),useDeferredValue 就能解決。

語法範例:
useDeferredValue 接受一個數值,並回傳一個「延遲更新」的新數值。

import { useState, useDeferredValue } from 'react';

function SearchPage() {
  const [query, setQuery] = useState('');
  // 將 query 轉換為低優先級的延遲值
  const deferredQuery = useDeferredValue(query);

  return (
    <div>
      {/* 1. Input 綁定原始 query,保持即時響應 */}
      <input value={query} onChange={(e) => setQuery(e.target.value)} />

      {/* 2. HeavyList 傳入 deferredQuery,React 會優先渲染 Input,*/}
      {/*    並在背景利用空閒時間渲染 HeavyList */}
      <HeavyList query={deferredQuery} />
    </div>
  );
}

四、 useTransition vs. useDeferredValue 對照表特性

https://ithelp.ithome.com.tw/upload/images/20260929/20178516bCdHgYggRo.png


上一篇
Day 14 Concurrent Mode 與 Fiber 架構:React 如何實現可中斷的渲染?
系列文
React 觀念架構:從js 基礎到Hook 底層邏輯 共 15 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言